#include "../inc/mem_leak.h"
#include "../inc/clist.h"

typedef struct MLeadMemNode {
    mu8 sFileName[MLEAK_FILE_NAME_LEN_BYTE_MAX];
    mu32 nLineNum;
    void* pMem;
    size_t nMemSize;
} MLeakNodeDef;



static void ShowMemBlockInfo(MLeakDef* cthis, MLeakNodeDef* pMemBlock);

mbool MLeak_Init(MLeakDef* cthis) {
    cthis->pRemaining = MNULL;
    cthis->pFreed = MNULL;

    cthis->pRemaining = malloc(sizeof (CListDef));
    cthis->pFreed = malloc(sizeof (CListDef));

    if (MNULL == cthis->pRemaining || MNULL == cthis->pFreed) {
        free(cthis->pRemaining);
        free(cthis->pFreed);
        return MFALSE;
    }

    CList_Init(cthis->pRemaining, MNULL);
    CList_Init(cthis->pFreed, MNULL);

    return MTRUE;
}

void* MLeak_Malloc(MLeakDef* cthis, size_t nSize, muc8* sFileName, mu32 nLineNum) {
    assert(MNULL != sFileName);

    void* pMem = MNULL;
    MLeakNodeDef* pUser = MNULL;
    CListDef* pNode = MNULL;



    if (strlen(sFileName) > MLEAK_FILE_NAME_LEN_BYTE_MAX) {
        fprintf(stderr,
                "#mem_leak[ERROR]: file name length mush less than %d bytes! ",
                MLEAK_FILE_NAME_LEN_BYTE_MAX);
        return MNULL;
    }


    pMem = malloc(nSize);
    if (MNULL == pMem) {
        goto error;
    }

    pUser = malloc(sizeof (MLeakNodeDef));
    if (MNULL == pUser) {
        goto error;
    }

    pNode = malloc(sizeof (CListDef));
    if (MNULL == pNode) {
        goto error;
    }


    pUser->pMem = pMem;
    pUser->nMemSize = nSize;
    strncpy(pUser->sFileName, sFileName, MLEAK_FILE_NAME_LEN_BYTE_MAX);
    pUser->nLineNum = nLineNum;

    CList_Init(pNode, pUser);
    CList_AppendToRight(cthis->pRemaining, pNode);


    return pMem;

error:
    //fprintf(stderr, "#mem_leak[ERROR]: malloc failed!");
    free(pMem);
    free(pUser);
    free(pNode);

    return MNULL;

}

void MLeak_Free(MLeakDef* cthis, void* pMem) {
    MLeakNodeDef* pUser = MNULL;
    CListDef* pNode = MNULL;

    if (MNULL == pMem)
        return;

    for (pNode = cthis->pRemaining->pRight;
            pNode != MNULL;
            pNode = pNode->pRight) {

        pUser = pNode->pUser;

        if (pUser->pMem != pMem)
            continue;

        CList_Remove(pNode);
        CList_AppendToRight(cthis->pFreed, pNode);
    }

    return;
}

void MLeak_ShowReport(MLeakDef* cthis, MLeakReportLevelDef eLevel) {
    mu32 nMemBlkAmount, nMemRemaining, nMemFreed;

    nMemRemaining = CList_LengthRigth(cthis->pRemaining);
    nMemFreed = CList_LengthRigth(cthis->pFreed);

    nMemBlkAmount = nMemRemaining + nMemFreed;

    printf("<<<<<<<<<<<<Memory Leak Report<<<<<<<<<<<<<<<<<<<\n");
    printf("---------------Summary--------------------\n");
    printf("Memory Block Amount: %u\n", nMemBlkAmount);
    printf("Remaining  Amount: %u\n", nMemRemaining);
    printf("Freed  Amount: %u\n", nMemFreed);

   
    
    
    if ((MLEAK_REPORT_LEVEL_REMAIN == eLevel) || (MLEAK_REPORT_LEVEL_FULL == eLevel) ) {
        if (nMemRemaining > 0) {
            printf("---------------Remaining List--------------------\n");
            for (CListDef* pNode = cthis->pRemaining->pRight;
                    pNode != MNULL;
                    pNode = pNode->pRight) {

                ShowMemBlockInfo(cthis, pNode->pUser);
            }
        }
    }
    
    if (MLEAK_REPORT_LEVEL_FULL == eLevel) {
        if (nMemFreed > 0) {
            printf("---------------Freed List--------------------\n");
            for (CListDef* pNode = cthis->pFreed->pRight;
                    pNode != MNULL;
                    pNode = pNode->pRight) {

                ShowMemBlockInfo(cthis, pNode->pUser);
            }
        }

    }

    //printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
}


//===========================================================

static void ShowMemBlockInfo(MLeakDef* cthis, MLeakNodeDef* pMemBlock) {
    assert(pMemBlock);

    printf("FileName: %s\t", (muc8*) pMemBlock->sFileName);
    printf("Line:\t %d\n", (muc8*) pMemBlock->nLineNum);
    printf("Mem:\t %p\n", pMemBlock->pMem);
    printf("MemSize: %zu\n", pMemBlock->nMemSize);

}
